/*
 * Copyright (c) 2012, Metron, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Metron, Inc. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL METRON, INC. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.metsci.glimpse.charts.shoreline;

import java.util.ArrayList;
import java.util.List;

/**
 * @author hogye
 */
public class LandSegmentFactory
{
    private final LandBox box;

    public LandSegmentFactory(LandBox box)
    {
        this.box = box;
    }

    /**
     * The returned LandSegment may have land on the wrong side. It is up
     * to the calling code to detect this and correct for it.
     */
    public LandSegment newLandSegment(List<LandVertex> vertices)
    {
        LandVertex start = vertices.get(0);
        LandVertex end = vertices.get(vertices.size()-1);

        if (start.equals(end)) return LandSegment.newFillableSegment(vertices);


        Edge startEdge = Edge.NONE;
        if (start.lat >= box.northLat) startEdge = Edge.NORTH;
        else if (start.lat <= box.southLat) startEdge = Edge.SOUTH;
        else if (start.lon >= box.eastLon) startEdge = Edge.EAST;
        else if (start.lon <= box.westLon) startEdge = Edge.WEST;

        Edge endEdge = Edge.NONE;
        if (end.lat >= box.northLat) endEdge = Edge.NORTH;
        else if (end.lat <= box.southLat) endEdge = Edge.SOUTH;
        else if (end.lon >= box.eastLon) endEdge = Edge.EAST;
        else if (end.lon <= box.westLon) endEdge = Edge.WEST;

        if (startEdge == Edge.NONE || endEdge == Edge.NONE) return LandSegment.newUnfillableSegment(vertices);


        List<LandVertex> ghostVertices = new ArrayList<LandVertex>();
        if (startEdge.isSame(endEdge))
        {
            // No ghost vertices needed
        }
        else if (startEdge.isOpposite(endEdge))
        {
            switch (startEdge)
            {
                case NORTH:
                case SOUTH:
                    ghostVertices.add(new LandVertex(end.lat, box.eastLon));
                    ghostVertices.add(new LandVertex(start.lat, box.eastLon));
                    break;

                case EAST:
                case WEST:
                    ghostVertices.add(new LandVertex(box.northLat, end.lon));
                    ghostVertices.add(new LandVertex(box.northLat, start.lon));
                    break;
            }
        }
        else if (startEdge.isAdjacent(endEdge))
        {
            switch (startEdge)
            {
                case NORTH:
                case SOUTH:
                    ghostVertices.add(new LandVertex(start.lat, end.lon));
                    break;

                case EAST:
                case WEST:
                    ghostVertices.add(new LandVertex(end.lat, start.lon));
                    break;
            }
        }

        return LandSegment.newFillableSegment(vertices, ghostVertices);
    }


    private static enum Edge
    {
        NONE, EAST, WEST, NORTH, SOUTH;

        public boolean isSame(Edge edge)
        {
            return (this != NONE && this == edge);
        }

        public boolean isOpposite(Edge edge)
        {
            switch (this)
            {
                case EAST:  return edge == WEST;
                case WEST:  return edge == EAST;
                case NORTH: return edge == SOUTH;
                case SOUTH: return edge == NORTH;
                default:    return false;
            }
        }

        public boolean isAdjacent(Edge edge)
        {
            switch (this)
            {
                case EAST:  return edge == NORTH || edge == SOUTH;
                case WEST:  return edge == NORTH || edge == SOUTH;
                case NORTH: return edge == EAST || edge == WEST;
                case SOUTH: return edge == EAST || edge == WEST;
                default:    return false;
            }
        }
    }

}
